/*
 *
 *  *    Copyright © OpenAtom Foundation.
 *  *
 *  *    Licensed under the Apache License, Version 2.0 (the "License");
 *  *    you may not use this file except in compliance with the License.
 *  *    You may obtain a copy of the License at
 *  *
 *  *         http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  *    Unless required by applicable law or agreed to in writing, software
 *  *    distributed under the License is distributed on an "AS IS" BASIS,
 *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  *    See the License for the specific language governing permissions and
 *  *    limitations under the License.
 *
 */

package com.inspur.edp.cef.engine.core.data.serializer;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.inspur.edp.cef.api.RefObject;
import com.inspur.edp.cef.designtime.api.IGspCommonDataType;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.engine.core.common.CefDataTypeUtil;
import com.inspur.edp.cef.entity.entity.IValueObjData;
import com.inspur.edp.cef.spi.jsonser.valueobj.AbstractValueObjSerializer;
import java.util.Date;
import java.util.function.Predicate;
import java.util.stream.Stream;
import lombok.var;

public class EngineSerializerItem extends AbstractValueObjSerializer {

  private IGspCommonDataType node;

  public EngineSerializerItem(IGspCommonDataType node) {
    this();
    this.node = node;
  }

  private EngineSerializerItem() {
  }

  protected IGspCommonDataType getDataType() {
    return node;
  }

//  private Object readAssociation(
//      GspAssociation asso, JsonParser reader, DeserializationContext serializer) {
//    if (reader.getCurrentToken() == JsonToken.VALUE_NULL) {
//      return null;
//    }
//    AssociationInfo info = createAssociationInfo(asso);
//    try {
//      reader.nextToken();
//      while (reader.getCurrentToken() == JsonToken.FIELD_NAME) {
//        String propertyName = reader.getValueAsString();
//        reader.nextToken();
//        if (asso.getBelongElement().getLabelID().equalsIgnoreCase(propertyName)) {
//          info.setValue(asso.getBelongElement().getLabelID(), readString(reader));
//        } else {
//          IGspCommonField element = CefDataTypeUtil.checkRefElementExists(asso, propertyName);
//          info.setValue(element.getLabelID(), readElementInfo(element, reader, serializer));
//        }
//        reader.nextToken();
//      }
////      reader.nextToken();
//      return info;
//    } catch (IOException e) {
//      throw new RuntimeException(e);
//    }
//  }
//
//  protected AssociationInfo createAssociationInfo(GspAssociation association) {
//    return new AssociationInfo(association);
//  }

//  private void writeAssociation(
//      GspAssociation asso,
//      String propName,
//      AssociationInfo assoValue,
//      JsonGenerator writer,
//      SerializerProvider serializer) {
//    try {
//      writer.writeFieldName(StringUtils.toCamelCase(propName));
//      if (assoValue == null) {
//        writer.writeNull();
//      } else {
//        writer.writeStartObject();
//        writeString(writer, assoValue.getValue(asso.getBelongElement().getLabelID()),
//            asso.getBelongElement().getLabelID(), serializer);
//        for (IGspCommonField refElement : asso.getRefElementCollection()) {
//          writeElementInfo(
//              refElement, assoValue.getValue(refElement.getLabelID()), writer, serializer);
//        }
//        writer.writeEndObject();
//      }
//    } catch (IOException e) {
//      throw new RuntimeException(e);
//    }
//  }

  @Override
  public void writeEntityBasicInfo(JsonGenerator writer, IValueObjData data,
      SerializerProvider serializer) {
    getElements().forEach(
        element -> {
          Object value = data.getValue(element.getLabelID());
          if (value instanceof Date) {
            this.writeDateTime(writer, value, element.getLabelID(), serializer);
          } else {
            this.writeAssociation(writer, value, element.getLabelID(), serializer);
          }
        });
  }

  private void writeElementInfo(
      IGspCommonField element, Object value, JsonGenerator writer, SerializerProvider serializer) {
    switch (element.getObjectType()) {
      case None:
        writeNormalElementInfo(element, value, writer, serializer);
        break;
//      case Association:
//        writeAssociationElementInfo(element, value, writer, serializer);
//        break;
      case Enum:
        writeEnumElementInfo(element, value, writer, serializer);
        break;
      default:
        throw new RuntimeException(
            "不支持序列化此字段类型" + element.getLabelID() + "." + element.getObjectType());
    }
  }

  private void writeEnumElementInfo(
      IGspCommonField element, Object value, JsonGenerator writer, SerializerProvider serializer) {
    writeString(writer, value, element.getLabelID(), serializer);
  }

//  private void writeAssociationElementInfo(
//      IGspCommonField element, Object value, JsonGenerator writer, SerializerProvider serializer) {
//    // writeAssociation(writer, value, element.getLabelID(), serializer);
//    GspAssociation asso = element.getChildAssociations().get(0);
//    writeAssociation(asso, element.getLabelID(), (AssociationInfo) value, writer, serializer);
//  }

  private void writeNormalElementInfo(
      IGspCommonField element, Object value, JsonGenerator writer, SerializerProvider serializer) {
    switch (element.getMDataType()) {
      case String:
      case Text:
        writeString(writer, value, element.getLabelID(), serializer);
        break;
      case Integer:
        writeInt(writer, value, element.getLabelID(), serializer);
        break;
      case Decimal:
        writeDecimal(writer, value, element.getLabelID(), serializer);
        break;
      case Boolean:
        writeBool(writer, value, element.getLabelID(), serializer);
        break;
      case Date:
      case DateTime:
        writeDateTime(writer, value, element.getLabelID(), serializer);
        break;
      case Binary:
        writeBytes(writer, value, element.getLabelID(), serializer);
        break;
      default:
        throw new RuntimeException(
            "无法识别的MDataType" + element.getLabelID() + "." + element.getMDataType());
    }
  }

  @Override
  public boolean readEntityBasicInfo(JsonParser reader, DeserializationContext serializer,
      IValueObjData data, String propertyName) {
    IGspCommonField element = getElements()
        .filter(item -> item.getLabelID().equalsIgnoreCase(propertyName))
        .findFirst().orElse(null);
    if (element == null) {
      return false;
    }
    data.setValue(element.getLabelID(), readElementInfo(element, reader, serializer));
    return true;
  }

  private Object readElementInfo(IGspCommonField element, JsonParser reader,
      DeserializationContext serializer) {
    if (element.getIsUdt()) {
      return readUdtElementInfo(element, reader, serializer);
    }

    switch (element.getObjectType()) {
      case None:
        return readNormalElementInfo(element, reader, serializer);
//      case Association:
//        return readAssociationElementInfo(element, reader, serializer);
      case Enum:
        return readEnumElementInfo(element, reader, serializer);
      default:
        throw new RuntimeException(
            "不支持序列化此字段类型" + element.getLabelID() + "." + element.getObjectType());
    }
  }

  private Object readUdtElementInfo(IGspCommonField element, JsonParser reader,
      DeserializationContext serializer) {
    var udtConfigId = CefDataTypeUtil.getUdtConfigId(element.getUdtID(), element.getUdtPkgName());
    return readNestedValue(udtConfigId, reader, serializer);
  }

  private Object readEnumElementInfo(
      IGspCommonField element, JsonParser reader, DeserializationContext serializer) {
    return readString(reader);
  }

//  private Object readAssociationElementInfo(
//      IGspCommonField element, JsonParser reader, DeserializationContext serializer) {
//    return readAssociation(element.getChildAssociations().get(0), reader, serializer);
//  }

  private Object readNormalElementInfo(
      IGspCommonField element, JsonParser reader, DeserializationContext serializer) {
    switch (element.getMDataType()) {
      case String:
      case Text:
        return readString(reader);
      case Integer:
        return readInt(reader);
      case Decimal:
        return readDecimal(reader);
      case Boolean:
        return readBool(reader);
      case Date:
      case DateTime:
        return readDateTime(reader);
      case Binary:
        return readBytes(reader);
      default:
        throw new RuntimeException(
            "无法识别的MDataType" + element.getLabelID() + "." + element.getMDataType());
    }
  }

  @Override
  public boolean writeModifyPropertyJson(
      JsonGenerator writer, String propertyName, Object value, SerializerProvider serializer) {
    //变更集的序列化, 0227与大家讨论后一致决定, 在序列化基类中对于seritem不处理的属性执行默认序列化, 所以此处
    //可以直接return false交由序列化基类处理.
    return false;
  }

  @Override
  public Object readModifyPropertyValue(
      JsonParser reader,
      DeserializationContext serializer,
      RefObject<String> propertyName,
      RefObject<Boolean> hasRead) {
    var element = getElements()
        .filter(item -> item.getLabelID().equalsIgnoreCase(propertyName.argvalue)).findFirst();
    if (!element.isPresent()) {
      hasRead.argvalue = false;
      return null;
    }
    hasRead.argvalue = true;
    propertyName.argvalue = element.get().getLabelID();
    return readModifyByElement(reader, serializer, element.get());
  }

  private Stream<IGspCommonField> getElements() {
    Stream rez = node.getContainElements().stream();
    if (getElementPredicate() != null) {
      rez = rez.filter(getElementPredicate());
    }
    return rez;
  }

  private Object readModifyByElement(JsonParser reader,
      DeserializationContext serializer, IGspCommonField element) {
    if (element.getIsUdt()) {
      var udtConfigId = CefDataTypeUtil.getUdtConfigId(element.getUdtID(), element.getUdtPkgName());
      return readNestedChange(udtConfigId, reader, serializer);
    }
    switch (element.getObjectType()) {
      case None:
        return readNormalElement(reader, element);
//      case Association:
//        return readAssociation(reader, element);
      case Enum:
        return readEnumElement(reader, element);
      default:
        throw new RuntimeException("暂不支持的字段类型:" + element.getLabelID());
    }
  }

  private Object readEnumElement(JsonParser reader, IGspCommonField element) {
    return readString(reader);
  }

  private Object readNormalElement(JsonParser reader, IGspCommonField element) {
    switch (element.getMDataType()) {
      case String:
      case Text:
        return readString(reader);
      case Integer:
        return readInt(reader);
      case Decimal:
        return readDecimal(reader);
      case Boolean:
        return readBool(reader);
      case Date:
      case DateTime:
        return readDateTime(reader);
      case Binary:
        return readBytes(reader);
      default:
        throw new RuntimeException(
            "无法识别的MDataType" + element.getLabelID() + "." + element.getMDataType());
    }
  }

//  private AssociationInfo readAssociation(JsonParser reader, IGspCommonField element) {
//    Objects.requireNonNull(element.getChildAssociations().get(0),
//        element.getLabelID() + ".getChildAssociations().get(0)");
//
//    if (reader.getCurrentToken() == JsonToken.VALUE_NULL) {
//      return null;
//    }
//    if (reader.getCurrentToken() != JsonToken.START_OBJECT) {
//      throw new RuntimeException("Association must start with ObjectToken");
//    }
//    try {
//      reader.nextToken();
//      AssociationInfo rez = new AssociationInfo(element.getChildAssociations().get(0));
//
//      while (reader.getCurrentToken() != JsonToken.END_OBJECT) {
//        String propertyName = reader.getValueAsString();
//        reader.nextToken();
//
//        IGspCommonField refEle = element.getLabelID().equalsIgnoreCase(propertyName) ? element
//            : element.getChildAssociations().get(0)
//                .getRefElementCollection().stream()
//                .filter(item -> item.getLabelID().equalsIgnoreCase(propertyName)).findFirst()
//                .orElse(null);
//        if (refEle == null) {
//          throw new RuntimeException("带出字段不存在" + element.getLabelID() + "." + propertyName);
//        }
//        rez.setValue(refEle.getLabelID(), readNormalElement(reader, refEle));
//        reader.nextToken();
//      }
//      return rez;
//    } catch (IOException e) {
//      throw new RuntimeException(e);
//    }
//  }

  protected Predicate<IGspCommonField> getElementPredicate() {
    return null;
  }
}
